home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / docs / tilefaq.12 < prev   
Text File  |  1995-04-20  |  34KB  |  695 lines

  1.  
  2.         =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  3.         -  Tile-Based Games FAQ version 1.2  =
  4.         =           by Greg Taylor           -
  5.         -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  6.  
  7. File     : tilefaq.12
  8. Home site: x2ftp.oulu.fi : pub/msdos/programming/docs
  9. Version  : 1.2
  10. Released : 4-20-95
  11.  
  12. Tilefaq 1.2 Copyright 1995 Greg Taylor.  All rights reserved.
  13. Appendix I  Copyright 1995 Chris Palmer.  All rights reserved.
  14.     This document is freely redistributable provided that is
  15.     distributed in its entirety, with this copyright notice 
  16.     included verbatim.  There are no restrictions on works 
  17.     derived from this document.
  18.  
  19.  
  20. -------------------------
  21. -  I : Introduction...  -
  22. -------------------------
  23. There has been a fair response to my initial release of this file
  24. and there have been many requests for additional information, all of
  25. which I will cover in this version.
  26.  
  27. This FAQ emphasizes on the style of graphics similar to those used
  28. in U6 and U7 by Origin.  Many of the techniques presented are aimed
  29. at systems with limited memory and/or speed like PCs with a 640K barrier;
  30. but this document also includes alternative methods and suggestions
  31. on how to code for less restrictive systems.  This is just a brief,
  32. but hopefully complete overview of one method to achieving the
  33. Tile-based style.  There are other methods and I'd like to hear about
  34. them, because much of this FAQ has been pieced together from various
  35. implementations of the 'Tile' graphics style.
  36.  
  37.  
  38. ---------------------------------
  39. -  II : Multiple layered maps.  -
  40. ---------------------------------
  41. This is an essential section to master because of the possibilities
  42. that stem from having more than one layer of map.  Almost all of your
  43. traditional effects can be more easily implemented with a multi-layer
  44. map as compared to a single layered one.
  45.  
  46. One of the key considerations when doing a multi-layer map is the
  47. speed of your drawing routines.  Since you may be drawing each tile
  48. several times, the speed at which that routine performs is vital to
  49. producing a fast game.  These should be coded in Assembly if possible
  50. or if in a higher level language, should be optimized as well as
  51. possible.
  52.  
  53. A 'SEE-THRU' tile placement routine is another important tool that
  54. is a major part of Tile-games.  I would separate my place-tile routine
  55. into two independent routines, one with 0 pixels 'SEE-THRU' and the
  56. other which doesn't.  This allows you to place tiles that don't have
  57. the need for the SEE-THRU option to be drawn faster.
  58.  
  59. -----------------------------------
  60. -  II i : SEE-THRU Tile routines  -
  61. -----------------------------------
  62. For those who do not have a SEE-THRU routine written and are wondering
  63. how you write one, here's a brief overview.  Basically, when you are
  64. copying your tile over, copy only the non-zero pixels to the screen
  65. (it doesn't -have- to be zero, it can be any of the palette values, but
  66. zero has become a sort of standard).  And when you draw your tiles,
  67. color the areas which you would like to be seen thru, with the zero
  68. color.  Thus allowing you to lay one tile over another, without
  69. destroying all of the image beneath.
  70.  
  71. A SEE-THRU routine is slower due to the check for the zero value,
  72. so it should only be used when necessary.
  73.  
  74. Another 'SEE-THRU' sort of technique I've seen used is what the programmer
  75. termed a 'SEE-FORTH' routine.  In this one he checked the destination
  76. pixel and only put the pixel color where there wasn't already a pixel there
  77. (ie the pixel location had a value of 0).  This routine is not as useful
  78. in tile games, but it is a possibility that I've seen used, so I thought
  79. I'd mention it.
  80.  
  81.  
  82. ---------------------------------
  83. -  II ii : The Multiple Layers  -
  84. ---------------------------------
  85. I use a three-layer map and it works fairly well for all of the things
  86. I do in my tile games.  A fourth layer can provide even more effects
  87. and a two layer map is possible as well, but I find three to be the
  88. optimum number.
  89.  
  90. I split the layers up as such...(these will be referenced to throughout
  91. the remainder of this text)
  92.  
  93.   Layer Name - The types of tiles used in each layer...
  94.       BASE    :  Grass, Dirt, Brick, Stone, Doors, Water...
  95.       FRINGE  :  Trees, Rocks, Tables...
  96.       OBJECT  :  Swords, Booty, People, Monsters, Keys...
  97.  
  98. A sample map variable declaration with three layers might be...(C code)
  99.  
  100.     #define SIZE 128
  101.  
  102.     typedef struct {
  103.          unsigned char base[SIZE][SIZE];
  104.          unsigned char frng[SIZE][SIZE];
  105.          unsigned char obj[SIZE][SIZE];
  106.     } maptype;
  107.  
  108. Or perhaps...(to address the layers numerically) (C code)
  109.  
  110.     typedef unsigned char maptype[3][SIZE][SIZE];
  111.  
  112. These are drawn on the screen in the order as listed above.  The BASE
  113. layer is drawn first, without the use of your SEE-THRU routine (Since
  114. it's the base).  Then you draw the FRINGE over the BASE using your
  115. SEE-THRU routine.
  116.  
  117. The FRINGE layer is about the most useful tool in producing powerful
  118. graphics easily.  A FRINGE tile might be a tree, with zero-values every
  119. where around the tree.  Then you could place the tree on any of the
  120. BASE tiles.  This allows you to have one tree drawing, but it can be
  121. a 'tree-on-grass' or a 'tree-on-dirt' or even a strange 'tree-in-the-
  122. water'.
  123.  
  124. Other possible FRINGE tiles are transitions.  These are like going
  125. >from grass-to-dirt or dirt-to-stone.  The FRINGE layer allows you to
  126. draw one set of transitions, for example grass, and then use those
  127. to do all of your grass-to-?? transitions.  This is a nice use of the
  128. FRINGE layer to save you from drawing endless tiles.
  129.  
  130. Tables and other non-pick-up-able objects are perfect for FRINGE, this
  131. way they can be placed on any BASE tile you like.  The possible uses of
  132. this layer of map are enormous.
  133.  
  134. After drawing both of the other layers, draw your OBJECT layer.  This
  135. layer is where you store things that move or can be picked up, etc.
  136. including monsters, keys, townspeople...  This makes it easy to pick
  137. up and put down objects without destroying other parts of your map.
  138.  
  139.  
  140. ---------------------------------------------------------
  141. -  III : Walkability - restricting character movement.  -
  142. ---------------------------------------------------------
  143. I usually assign an attribute I call the 'walkability' to my BASE tiles.
  144. This provides a fast, easy, way to check whether you can/cannot move
  145. to a certain space, and it also helps you to control other special
  146. occurrences with a relative level of ease.
  147.  
  148. At each position in my map arrays, I have a byte (unsigned char) value
  149. which serves as both the tile-index and the walkability value.  I use
  150. a set of 128 tiles, and split them up as such...
  151.  
  152.     0-127
  153.      0-63    : Normal, walkable tiles, dirt, grass etc.
  154.      64-127  : Normal, unwalkable tiles, walls, etc.
  155.     128-255
  156.      128-191 : Special tiles, group 1
  157.      192-255 : Special tiles, group 2
  158.  
  159. When I'm drawing the screen, I simply use the REM (or MOD) statement or
  160. equivalent to get the proper value, by MODing the number by 128.  This
  161. gives a value from 0-127, which is the actual tile-index number.  When it
  162. comes to checking if that tile is 'walkable', you then would divide the
  163. number by 64, yielding a value of 0, 1, 2, or 3...
  164.  
  165.     0 : Walking is OK
  166.     1 : Walking is not OK
  167.     2 : Special thing happens when they step here - group 1
  168.     3 : Special thing happens when they step here - group 2
  169.  
  170.  
  171. The first two values are simply understood, but the special values might
  172. need some explaining...This allows you to program in special occurrences
  173. that happen when that space is walked on.  When it hit's a special square
  174. for instance, you would check through the special spots list for the x,y
  175. coords of the spot that triggered the special occurrence and the level map
  176. that it is on.  This allows an easy way to throw cool stuff into your
  177. game with little work.  Why it is split into two groups is so that you
  178. need not search ALL of your specials for that particular map at once,
  179. searching for the effects of that one.
  180.  
  181. You will note that the WA (Walkability) value of 3 represents the section
  182. of tiles which are unwalkable normally (like walls etc.)  These can make
  183. for excellent 'secret' walls and so forth.
  184.  
  185. The walkability setting can also be stored as a separate element of your
  186. map structure, to increase speed, at the expense of memory.  Having it
  187. as a separate element allows you to include many more than 4 settings
  188. to the rating, allowing for 'level exits' and so forth without having
  189. to resort to listing them as 'specials'.  The method I list above with
  190. the byte being split into the various categories is the most general
  191. compromise between, ease, speed, and memory, I have come up with; but on
  192. systems where memory is not much of a constraint, having walkability stored
  193. in a seperate element of the map structure is usually a better way to go.
  194.  
  195. More mention is made to the 'Walkability' values later in the text.
  196.  
  197.  
  198. ----------------------------------
  199. -  VI : Disappearing Roof Tiles  -
  200. ----------------------------------
  201. This effect can be done using my multi-layer method by simply sectioning
  202. off a few of your base tiles (say 48-63 for example) as 'FLOOR' types.
  203. These would have another tile in memory as well as their normal tile,
  204. for when those floors are covered.  In general, all of the FLOOR types
  205. will be covered most of the time.  When drawing your screen and come
  206. upon a tile that is a FLOOR tile, then you'd check to see if the player
  207. was standing on a tile of that type...   If not, draw -only- the alternate
  208. 'ROOF' tile which corresponds to that FLOOR tile.  If the player -is-
  209. standing on a FLOOR tile of that type, draw the BASE, FRINGE and OBJECT
  210. layers normally.  This way you can have only the roofs where the player
  211. is, disappear when they enter a building.  (also see Appendix I)
  212.  
  213.  
  214. ------------------------------------------------
  215. -  V : Tilted effects, using the FRINGE Layer  -
  216. ------------------------------------------------
  217. I like the 'tilted' look in my tile projects, it gives a bit more of a
  218. realistic flavor.  If you have the memory, the best way to achieve this
  219. effect is to set aside a 4th layer to your map, called the TILT layer
  220. or something (it can also be used for ROOF file management if you like,
  221. think about it :)  ).  But since most people don't have the memory for
  222. four map layers in memory, I'll discuss the memory-deficient method.
  223.  
  224. Just draw the main portion of your tilted walls as your BASE layer
  225. tiles, then use the FRINGE layer to hold the extra bits that tilt off of
  226. the tile.  You would have to do a special check to see if the FRINGE
  227. layer tile in question is a tilt-result or a normal FRINGE tile, because
  228. of the order of drawing.  If it's a tilt-result, then you would want to
  229. draw the OBJECT layer and the PLAYER before drawing the tilt-result
  230. tile; and if not you'd follow the normal order of BASE-FRINGE-OBJ.
  231.  
  232. This is where the 4th TILT layer makes like easier, for those who have
  233. the memory to use for it.  It allows you to skip this check and just
  234. draw in the normal order, since your normal FRINGE and tilt-results
  235. are already split up...
  236.  
  237.  
  238. ------------------------------------------------
  239. -  VI : General comments on the OBJECT layer.  -
  240. ------------------------------------------------
  241. The OBJECT layer in my projects is an array the same as the other layers
  242. of the map, of unsigned characters (or bytes).  These have a value of
  243. 0 to 255, by the variable size.  I find this to be enough objects to
  244. cover my needs.  Each number would be an index to a particular object,
  245. 0 meaning there's no object in that map-space.  I split the byte up into
  246. various object categories...for example 1-127 would be monsters and towns
  247. people, 128-255 for inanimate objects...whatever.  Anyway, I like to have
  248. an 'intelligence' (much like walkability) assigned to various groups of
  249. objects.
  250.  
  251. These are usually broken into groups of 16, for the ease of the math to
  252. get the values...Below is an example break down of 'Intelligence' of
  253. objects (more info on this style of attribute, see the 'Walkability'
  254. section)...
  255.  
  256. INT    Index    :   What behavior is exhibited by the Object...
  257.  0      0-15    : Townspeople...wander aimlessly...
  258.  1      16-31   : Townspeople/Monsters who are afraid of the character.
  259.  2      32-47   : Docile Monsters, wander aimlessly until attacked, at
  260.           which point their INT is switched to...3...
  261.  3      48-63   : Same Docile Monster pictures, but now they're mad!
  262.  4-5    64-95   : Normal monsters, they charge at a slow pace...
  263.  6      96-111  : Baddie monsters, they charge right at you..
  264.  7      112-127 : Projectile firing monsters...
  265.  
  266.  8      128-143 : Keys, and other door-opening things.
  267.  9      144-159 : Weapon objects...
  268.  10     160-175 : Armor and the like...
  269.  11     176-191 : Cash, and other booty.
  270.  12-13  192-223 : Normal, plain objects, like books and candles.
  271.  14     224-239 : Some other Obj category...
  272.  15     240-255 : Objs that hold other objs...bags, chests, backpacks.
  273.  
  274. The above is just a sample chart of how you might choose to lay out
  275. your OBJECTS to get the most efficient use of the INT value.  I like
  276. using an Intelligence to keep track of behavior of OBJECTS.  Thus in
  277. order to do the proper things for each OBJECT I would simple have to
  278. check that object's INT and then do what I need to do for that OBJ.
  279. It's helpful...understand?  I hope so.
  280.  
  281. Many large projects will find that 255 just isn't enough objects, in
  282. these cases, you'd be best advised to move to an array of unsigned
  283. short variables (short ints...16bits)  this allows for a value from
  284. 0 to 65535.  That should be enough objects for any game I've ever
  285. played!
  286.  
  287.  
  288. ------------------------------------------
  289. -  VII : Multiple OBJECTs on one space.  -
  290. ------------------------------------------
  291. The question was raised when I was discussing my methods with another
  292. programmer, how do you handle multiple OBJECTs in one space?  I never
  293. really thought much about it before and just restricted OBJs to one
  294. per space.  The simplest method I came up with is special INT (see above
  295. section) values for OBJs that hold other objects.  These are things
  296. like bags, backpacks, treasure chests, etc.  In the example above
  297. this is category 15, Indexes 240-255.  The objects would have a picture
  298. assigned to them as normal, but they would each have an independent
  299. array of other OBJECTS that they hold.  Each of them could have a
  300. certain max set by your particular array structures.  This way, when
  301. you pick up those objects, -all- of the object list gets added to your
  302. inventory.  When there is a chest or bag on the ground you could also
  303. drop a number of OBJs there and have them be filed off to the independent
  304. array for that bag or chest.
  305.  
  306. This method is a good way to incorporate a way to have multiple objects
  307. in one map space, without having a huge amount of additional map layers.
  308. It's relatively speedy, and still memory efficient.  Please note that
  309. the maximum number of bags and other such mult-OBJ-objects, are limited
  310. in number by the number of array structures that you assign to them, so
  311. never include more than the number that you can handle on one map.
  312.  
  313.  
  314. Often times the above method is too restrictive or doesn't match the play
  315. style of the game.  The alternate method is a bit more complicated and
  316. requires a knowledge of the use of 'linked-lists'.  If you aren't familiar
  317. with linked-lists, pick up nearly any intro-book for your programming
  318. language of choice and look up linked-lists on the index...you should find
  319. it.  Assuming a knowledge of linked-lists, I'll continue.
  320.  
  321. Change your object layer to an array of list pointers.  Then as you place
  322. objects in a map-location, add a node to the list at that location.  When
  323. objects are removed, remove the node.  This will allow for an unlimited
  324. (well, memory limited) number of objects on any particular map-location.
  325.  
  326.  
  327. --------------------
  328. -  VIII : Cool FX  -
  329. --------------------
  330. This section discusses some random cool effects I've come across, that
  331. are relatively simple to implement and can really improve the 'look and
  332. feel' of the game.
  333.  
  334. One such effect that I like doing rotating palettes.  This is good for
  335. flowing water in streams and smoking chimneys.  You just run a rotating
  336. palette which will change certain colors in a certain order which
  337. produces good FX without much added programming time...
  338.  
  339.  
  340. Also another cool effect is to animate your tiles, this can be done by
  341. an array of pictures instead of just one being assigned to a tile; and
  342. then incrementing thru the array during your playloop.  For example you
  343. might have a section of your FRINGE layer be animated tiles, one of which
  344. is a 'fire'.  This would rotate thru say...4 frames of a fire burning and
  345. smoking etc....providing a nice effect for the player.  Animating people/
  346. monsters is also a nice addition for a better effect.
  347.  
  348.  
  349. For those who are confident with palette manipulation routines, another
  350. good effect can be achieved by lightening and darkening the palette.  For
  351. instance, a player is in a cave, with only a torch providing the light,
  352. you could set up your palette so that as the tiles get further from the
  353. light source (the torch) the darker they are drawn.  A good way to do this
  354. is to make a palette of say...64 colors and then have 4 copies of that
  355. palette to make up your 256 color palette.  A simple shift by 64 will
  356. lighten or darken a whole tile.
  357.  
  358. Another way to achieve this same effect, but staying with a single palette
  359. of 256 colors, is to create several 'reference-palette's.  Sort through
  360. your palette and create a cross-referenced palette for each darkness
  361. level you want.  Take each color on the palette and darken it the desired
  362. ammount, then search the palette for the best match and keep that color's
  363. index as the cross-reference value.  These reference palettes can all be
  364. calculated beforehand and stored to disk, so no real run-time slowdown is
  365. introduced.  When drawing a 'shaded-tile' (might be one of your settings in
  366. your DrawTile routine along with SEE-THRU) check the appropriate darkness
  367. cross-reference palette for each pixel value and draw the cross-referenced
  368. value to the screen.  This method is superior to the above method in that
  369. it allows for much more dramatic shades and colors, but it's drawback
  370. is that it's slower (do to the checking for -what- shade to make each
  371. square, the actual drawing of a shaded square is just slightly slower).
  372.  
  373. Either of the above methods are good ways to do shadows and passing clouds
  374. overhead etc.  As alluded to in the example, they also provide a great way
  375. to create a 'torch-light' effect, where the tiles fade to black as they
  376. get further from the light source.  You could also fade to a light grey for
  377. a good 'fog' effect.  If you are implementing a limited-display as described
  378. in Appendix I of this document, you may want to combine the two algorythms
  379. into one, to improve efficiency.
  380.  
  381.  
  382. ------------------------------------------------
  383. -  IX : Smooth Loading of new map sections...  -
  384. ------------------------------------------------
  385. This question comes up a lot.  My way of dealing with it is splitting
  386. my map into a LOT of little sections within my map-file.  I load nine
  387. sections of that map into memory at one time....
  388.  
  389.     The Map Chunks in Memory.
  390.     /-------+-------+-------\
  391.     |       |       |       |
  392.     |       |       |       |
  393.     |       |       |       |
  394.     +-------+-------+-------+
  395.     |       | Where |       |
  396.     |       | Player|       |
  397.     |       | Is... |       |
  398.     +-------+-------+-------+
  399.     |       |       |       |
  400.     |       |       |       |
  401.     |       |       |       |
  402.     \-------+-------+-------/
  403. Then when the player moves into a new section of the map, shift six
  404. sections of map over in memory, then load in the three new sections.
  405. This makes for smooth scrolling with no edges, without extremely long
  406. load times.
  407.  
  408. Your on-disk map can be incredibly large, in fact, the only limit is the
  409. ammount of disk space you have (or variable addressing, that is, if you
  410. exceed a 4gig x 4gig map :), the in-memory map is only a little window of
  411. that, then the displayed map is yet another subset window in that.  On
  412. standard memory limit systems (like dos, 640k barrier) you can set your
  413. in-memory map to a fixed size.  But when you have access to variable
  414. ammounts of memory, it's usually best to adjust to the available memory.
  415. Thus calculating the dimensions of your in-memory map to conform with
  416. the memory available.  This way if a user has a lot of memory, they can
  417. benefit with load times occuring less often.  This method pleases the
  418. player with more memory (loads less often) but is a bit of a headache
  419. to code; the variable size mapsegmenting is tricky.
  420.  
  421.  
  422. -----------------------------------------
  423. -  IX : Portability and Speed vs. Size  -
  424. -----------------------------------------
  425. This section is more of a discussion on programming style and suggestions
  426. concerning that, but mention of it here may be useful for many tile-coders.
  427.  
  428. When coding any project, it is generally a good idea to keep that code
  429. as 'portable' as possible.  This loosely put means that you code using 
  430. 'standard' functions and routines, and try to avoid using system or 
  431. compiler specifics in your code.  I've run up against this head-on just 
  432. lately as I bought a new compiler which is 32-bit (as apposed to the
  433. 16-bit compiler I used before), and had to go through my code and completely
  434. revise it to work under the new system.  One of the main problems was my
  435. use of the type 'int' (integer, I code in C mostly), which is 16-bit
  436. on some systems, but 32-bit on others.  To solve portability problems I've
  437. now gone to rarely, if ever, using 'int' but in it's stead use 'short'
  438. (a short integer, 16-bits) and 'long' (a long integer 32-bits), which are
  439. the same under all of the compilers I use.  
  440.  
  441. Also, many languages allow you to split your code into seperate chunks or 
  442. in the more formal circles known as 'units' or 'packages'.  I split my
  443. code two ways: one section is my standard library of game functions (my
  444. fxlib) and the other section is the code for whatever game I'm working on.
  445. This way I can save myself the trouble of cut-and-pasting code and some
  446. of the problems that come with that, and just stick with my standard library
  447. for those functions.
  448.  
  449. Along the lines of system specifics and segmentation of code, it is usally
  450. best to stuff any system-dependent code off in one library or unit, so
  451. that you only need to recode that one unit when porting the code to another
  452. compiler/system.  Examples of system-specific code are : graphics, 
  453. controller (mouse, keyboard, joystick), timing and of course assembly 
  454. (another porting problem I had...).
  455.  
  456. With some extra effort spent learning about portability, you can prevent
  457. a *lot* of wasted time later revising code...
  458.  
  459.  
  460. SIZE versus SPEED, the endless struggle.  Though computers are getting faster
  461. and have more memory, size and speed are still at odds and a balance must be
  462. struck between them.  There are many ways of going about coding various
  463. parts of a game, each of which has varying size (memory used) and speed (how
  464. fast they go).  What each programmer must decide is what memory they must
  465. sacrifice in order to gain added speed, or what speed they must sacrifice
  466. to shrink the ammount of memory used.  The methods described in this file
  467. have been devised to generally strike a pretty good balance between size and
  468. speed, though you can go either way with them, tuning them for smaller size
  469. or tuning them for faster exicution.  You'll have to use your own descretion
  470. on what balance you want to strike, but I think that the methods in this
  471. faq are pretty close to the optimal 'middle-ground'.
  472.  
  473.  
  474. -------------------------------------------
  475. -  X : Last Minute Ideas...and thoughts.  -
  476. -------------------------------------------
  477. Well I guess that's it for this version of the FAQ.  It's not really
  478. laid out in the Standard Question/Answer method, but it is in reasonable
  479. categories to assist you in finding the info you want.  Keep in mind
  480. that this is just a summary of my method of Tiley-games, and thus there
  481. are other (probably better) methods out there.  My methods are
  482. continuously growing and shifting, due to questions people ask me or
  483. effects I see in other games, so if you've got any ideas I might be
  484. interested in hearing them.
  485.  
  486. I've received some requests for some of my finished games using this
  487. method...unfortunately, like so many programmers, I have not finished
  488. a single Tile-RPG game.  I always get a new idea for a better way to
  489. do things half-way thru and start fresh...going nowhere.  But thru the
  490. hordes of half-projects I've developed a method that works well.  I've
  491. also been requested to put together a demo of my methods.  I will likely
  492. do such, but currently I'm very busy.  When I do write up some sample
  493. code, I'll post it at x2ftp.oulu.fi as well.
  494.  
  495. I do have one Shareware game currently on the market, it uses a small
  496. offshoot of my tile-method; not nearly as complicated as the method
  497. presented in this file.  My current project(s) include directing a
  498. multi-continental (literally) game project which will be implementing
  499. a form of Genetic Algorhythms (Alife simulations) and the other is a
  500. tile-based strategy wargame (with no name yet).  This game (when I get
  501. it finished) will demonstrate several of the methods discussed in this
  502. document, amoung them : a 3-layer map, palette rotation for cool fx,
  503. a single directional tilt, and other neat tile-stuffs.
  504.  
  505. I hope this FAQ gives a good enough summary of basic Tile-Game concepts
  506. to get you started/finished with your programming projects.  Have fun!
  507.  
  508. I can be reached for questions/comments/additions/etc. via email at :
  509.  
  510.     gtaylor@oboe.aix.calpoly.edu
  511.  
  512. The latest version of this FAQ can be found at :
  513.  
  514.     x2ftp.oulu.fi   pub/msdos/programming/docs/tilefaq.*
  515.  
  516.  
  517. May you code for many days and never have a bug.  -=GT=-
  518.  
  519.  
  520. ----------------------------------------
  521. -  APPENDIX I : Limiting the display   -
  522. ----------------------------------------
  523. A common problem to most tile based games is "what can the player see?".
  524. For example, in a dungeon setting you must be very careful to limit
  525. what is shown to the player or else there is just no point including
  526. secret doors.
  527.  
  528. Map:                         Display:
  529. *************                *********    [ assume that S is a secret
  530. *...*.......*    ===>        *.......*      door and most likely looks
  531. *...S.......*                S.......*      like the rest of the walls ]
  532. *************                *********
  533.  
  534. I've come up with my method of choice which anyone is free to dispute
  535. with me or to offer up a better solution.  This algorith is O(n) with
  536. a moderate constant (that is, the algorithm looks at each square only
  537. once and doesn't have a particularily large or small overhead).
  538.  
  539. You need one extra piece of information in your map (which hasn't
  540. been discussed in the tileFAQ) which is opaqueness of each square.
  541. That is you need to be able to get a value of:
  542.     1 = You cannot see through this square.  This does not mean that
  543.         the square is never visible just that things "behind" it won't
  544.         be visible.
  545.     0 = You can see through this square.
  546. It does not matter how you store this information.  Where this algorithm
  547. came from I defined all my objects to have many attributes one of which
  548. was opaqueness.
  549.  
  550. [ Editor's Note (GT) - I would implement the opaque values as a attribute
  551.   of each tile, thus keeping an array of opaque values (say ...
  552.   opaq[MAXTILES]) which is indexed by the same index as the tiles.  So
  553.   in checking the opaque attribute, you wouls simple have to take the
  554.   tile value (say ... 0..255) from the map position in question, and use
  555.   that value to index the opaque array.
  556.  
  557.   For multiple layered maps you can just use the opaqueness of the base
  558.   tiles and ignore any of the higher levels.  However, to offer yourself
  559.   more variety in the effects, you could balance 1, 2, perhaps 3.  It's
  560.   also important to note that even when checking three values (BASE,
  561.   FRINGE, OBJECT) of opaque attriibutes, if any of them are non-opaque,
  562.   then the whole tile is non-opaque. ]
  563.  
  564. I'll be using a standard coordinate system where the map is located
  565. on a cartesian plane and i'll be using (x,y) as a normal notation.
  566. I'm assuming that the player is at position (o_x, o_y) and that you
  567. want to draw the map with the player in the center of the square
  568. and with a radius of DELTA (that means that you want to draw DELTA*2+1
  569. by DELTA*2+1 tiles).
  570.  
  571. For any pedantic readers: define the radius of a square as being
  572. the length of any orthogonal vector from the origin to the square.
  573. Throughout the remainder of this explanation i'll include the
  574. "pedantic people" comments in square brackets.  If you don't care,
  575. then don't read the information in square brackets.
  576.  
  577. For the non-pedantic readers, we'll build successively larger squares
  578. starting with the squares one space from the origin.
  579.  
  580. For any given point (x,y) we will approximate whether or not it is
  581. viewable by finding one or two points that lie on the previous square
  582. [Let R = radius of the square containing (x,y), find (x1,y1), (x2,y2)
  583. which lie on the square of radius R-1] between (x,y) and the origin.
  584. It turns out that the statement "one or two" points is easiest to
  585. implement if we always have two points.  For any point which lies in
  586. a horizontal, vertical or diagonal line from the origin we will simply
  587. use the same point twice.
  588.  
  589. The one last thing that we need is a sign function (not sine).  For
  590. those who don't happen to know what that is
  591.             |u|
  592.     sign(u) = -------,  for all non-zero u, let sign(0) = 0.
  593.              u
  594.  
  595. [ Editor's Note (GT) - The strictly defined formula as stated above is
  596.   not the best way to implement it in a program, because divides are
  597.   a slow operation.  You can reach the sign value of an integer-based
  598.   data-type by a simple bitshift by n-1 bits (e.g. for an 16-bit
  599.   integer, shift it right by 15 to get the sign bit).  Or you could
  600.   also implement a sign function by the following code (C) :
  601.         if (u>0) sign=1;
  602.         else if (u < 0) sign=-1;
  603.         else sign = 0; ]
  604.  
  605. To restate, assume that we have origin (o_x, o_y) and point (x,y).
  606. Let (i, j) = (x - o_x, y - o_y) [be the vector from (o_x, o_y) to (i,j)]
  607. We can then easily calculate the two points as:
  608.  
  609.     point_1 =   (-1 * sign (i) + x, -1 * sign (j) + y)
  610.                  { (x,-1 * sign (j) + y)     IF |j| > |i|
  611.     point_2 = { (-1 * sign (i) + x, y)  IF |j| < |i|
  612.                  { point_1 IF |j| = |i|
  613.  
  614. [ point_1 is in the diagonal direction from (x,y) to (o_x,o_y) and
  615.   point_2 is in the horizontal/vertical direction from (x,y) to (o_x, o_y).
  616.   Pretty easy to prove that that statement is true and from that you
  617.   can convincingly assert that this provides an good O(n) determination
  618.   of which squares are blocked from view.
  619.   Notice that the definition of sign(0)=0 means that point_2 collapses to
  620.   point_1 if j = 0 or i = 0 which is why i've decided to always use two
  621.   points.  Well, that and the use of the constant 2 in the algorith,
  622.   see the comments after the algorithm. ]
  623.  
  624. >From the calculation of those two points it because almost criminally
  625. easy to decide which tiles can be seen and which cannot.
  626.  
  627. Let opaque be an array DELTA*2+1 by DELTA*2+1 which undefined value
  628. (ie: you don't have to initialize it).  Remember that DELTA is the number
  629. of tiles in any direction [radius of the display] that we will be drawing.
  630.  
  631. Here's the pseudo-code of how to do it:
  632.  
  633. { cheat and do the case of delta=0 so that we don't have to worry
  634.   about any kind of special case }
  635.   middle = DELTA+1             { This is the middle of the display }
  636.   opaque[middle][middle] = 0   { delta=0 wasn't so hard :-) }
  637.  
  638.   FOR delta = 1 TO DELTA DO
  639.     FOR each (x,y) that lie on the square of radius delta
  640.       Calculate the two points as described above, call them
  641.     p1_x, p1_y, p2_x, p2_x.
  642.       Make sure that (p1_x,p1_y) and (p2_x,p2_y) are on the map.
  643.       IF Opaque[p1_x - o_x + middle][p1_y - o_y + middle] +
  644.      Opaque[p2_x - o_x + middle][p2_y - o_y + middle] >= 2     I
  645.       THEN
  646.     { You can't see this square }
  647.     Opaque[x - o_x + middle][y - o_y + middle] = 1        II
  648.       ELSE
  649.     Opaque[x - o_x + middle][y - o_y + middle] = ???      III
  650.     { You might want to draw the tile now if you can }
  651.       ENDIF
  652.     ENDFOR
  653.   ENDFOR
  654.  
  655. That looks a lot more complicated than it really is.  The hardest part
  656. in implementing that loop is the "FOR each (x,y) that ..." line.
  657. If you are a little creative you can do that easily enough.
  658.  
  659. On line I and II the constants 2 and 1 are used to give the algorithm a
  660. little flexibility.  By setting the opaqueness of unviewable squares to 1
  661. and requiring that both "blocking" squares to be opaque (the value 2) the
  662. algorithm will allow for looking "around" minor obstacles.  To make the
  663. routine much more strict you could use a value of 2 on line II which
  664. will often give more realistic displays but (IMHO) less playable
  665. results.
  666.  
  667. If you would like a more detailed explanation of the derivation of the
  668. two points or something a pretty close to an actual C implementation
  669. (I have my first attempt at writing this appendix which was far too
  670. formal but did have some code with it) you can send me an email and
  671. politely ask me to forward it to you or if you have a web browser
  672. (mosaic, netscape, lynx) you could find both documents at
  673.  
  674.     http://noether.math.uwaterloo.ca/~crpalmer/
  675.  
  676. Any questions/comments/criticisms can be directed to me via email at:
  677.  
  678.     crpalmer@undergrad.math.uwaterloo.ca
  679.  
  680.  
  681. /=====================================================================\
  682. | Revision History...                                                 |
  683. |---------------------------------------------------------------------|
  684. | 1.0 : Initial Release - Basic info on my method for Tiley-games.    |
  685. |---------------------------------------------------------------------|
  686. | 1.1 : Added clarifications, especially a more in depth look at      |
  687. |       memory structures.  Added several new methods to the list.    |
  688. |---------------------------------------------------------------------|
  689. | 1.2 : Touched it up a bit, added porting/size/speed and Appendix I. |
  690. \=====================================================================/
  691. Thanks to Gabor Torok and Scott Host, who's methods have influenced those
  692. in this document (as well as countless tile-based games which I've examined).
  693.  
  694.  
  695.